﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;
using Services;
using System.IO;

namespace SpaceSlideShow
{
  /// <summary>
  /// This is the main type for your game
  /// </summary>
  public class Game1 : Microsoft.Xna.Framework.Game
  {
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;

    List<ISlide> slides;
    int current = 0;

    public Game1()
    {
      graphics = new GraphicsDeviceManager(this);
      Content.RootDirectory = "Content";

      graphics.IsFullScreen = true;
      graphics.PreferredBackBufferWidth = 1024;
      graphics.PreferredBackBufferHeight = 768;
    }

    /// <summary>
    /// Allows the game to perform any initialization it needs to before starting to run.
    /// This is where it can query for any required services and load any non-graphic
    /// related content.  Calling base.Initialize will enumerate through any components
    /// and initialize them as well.
    /// </summary>
    protected override void Initialize()
    {
      Components.Add(new Background(this));

      slides = Slides.VENICE_CAMP_2010_Slides.ToList();
      current = 0;

      try
      {
        using (var sr = new StreamReader("last_slide.txt"))
        {
          current = int.Parse(sr.ReadLine());
        }
      }
      catch (Exception e) { }

      this.Exiting += new EventHandler<EventArgs>(Game1_Exiting);

      base.Initialize();
    }

    void Game1_Exiting(object sender, EventArgs e)
    {
      using (var sw = new StreamWriter("last_slide.txt"))
      {
        sw.WriteLine(current.ToString());
      }
    }

    SpriteFont font, title;
    IBackground background_svc { get { return Services.GetService(typeof(IBackground)) as IBackground; } }
    protected override void LoadContent()
    {
      // Create a new SpriteBatch, which can be used to draw textures.
      spriteBatch = new SpriteBatch(GraphicsDevice);

      font = Content.Load<SpriteFont>("OCRAExtended28");
      title = Content.Load<SpriteFont>("OCRAExtended32");
    }

    /// <summary>
    /// UnloadContent will be called once per game and is the place to unload
    /// all content.
    /// </summary>
    protected override void UnloadContent()
    {
      // TODO: Unload any non ContentManager content here
    }

    KeyboardState perv_kb = Keyboard.GetState();
    bool was_transitioning = false;
    protected override void Update(GameTime gameTime)
    {
      var sane_kb = Keyboard.GetState();

      if (was_transitioning && background_svc.IsRotating == false && current == slides.Count - 1 ||
        (sane_kb.IsKeyDown(Keys.B) && perv_kb.IsKeyUp(Keys.B)))
        StartShipFlyOver();

      if (sane_kb.GetPressedKeys().Any())
        System.Diagnostics.Debug.WriteLine((from k in sane_kb.GetPressedKeys() select k.ToString()).Aggregate("", String.Concat));

      if (sane_kb.IsKeyDown(Keys.PageDown) && perv_kb.IsKeyUp(Keys.PageDown)
        && background_svc.IsRotating == false)
      {
        if (current < slides.Count - 1)
        {
          current++;
          if (slides[current] is TextSlide)
            background_svc.StartRotation();
        }
      }
      if (sane_kb.IsKeyDown(Keys.Home) && perv_kb.IsKeyUp(Keys.Home) && background_svc.IsRotating == false)
      {
        current = 0;
        background_svc.StartRotationInverted();
      }
      if (sane_kb.IsKeyDown(Keys.End) && perv_kb.IsKeyUp(Keys.End) && background_svc.IsRotating == false)
      {
        current = slides.Count - 1;
        background_svc.StartRotation();
      }
      if (sane_kb.IsKeyDown(Keys.PageUp) && perv_kb.IsKeyUp(Keys.PageUp) && background_svc.IsRotating == false)
      {
        if (current > 0)
        {
          if (slides[current] is TextSlide)
            background_svc.StartRotationInverted();
          current--;
        }
      }
      perv_kb = sane_kb;
      was_transitioning = background_svc.IsRotating;
      base.Update(gameTime);
    }

    float MAX_SHIP_FLYOVER_TIME = 3.0f;
    class ShipFlyover
    {
      public Model model;
      public float ship_flyover_time;
      public Vector3 start, end;
      public Matrix transform;
    }
    List<ShipFlyover> ship_flyovers = new List<ShipFlyover>();
    Random random = new Random();
    private void StartShipFlyOver()
    {
      Model model = null;
      switch (random.Next(5))
      {
        case 0:
          model = Content.Load<Model>(@"Capital Ships\yellow_fighter");
          break;
        case 1:
          model = Content.Load<Model>(@"Capital Ships\p_sf01");
          break;
        case 2:
          model = Content.Load<Model>(@"Capital Ships\corvette\model");
          break;
        case 3:
          model = Content.Load<Model>(@"Capital Ships\e_sf11");
          break;
        case 4:
          model = Content.Load<Model>(@"Capital Ships\small_carrier");
          break;
      }

      var transform = Matrix.CreateFromYawPitchRoll(0.0f, 0.0f, (float)random.NextDouble() * MathHelper.TwoPi);

      var start = Vector3.Right * 20.0f + Vector3.Down * 4.0f;
      var end = Vector3.Left * 20.0f + Vector3.Up * 1.0f;

      start -= Vector3.Forward * 5.0f * ((float)random.NextDouble() - 0.5f);
      end += Vector3.Forward * 5.0f * ((float)random.NextDouble() - 0.5f);

      start += Vector3.Up * 5.0f * ((float)random.NextDouble() - 0.5f);
      end += Vector3.Up * 5.0f * ((float)random.NextDouble() - 0.5f);

      ship_flyovers.Add(
        new ShipFlyover()
        {
          ship_flyover_time = MAX_SHIP_FLYOVER_TIME,
          model = model,
          start = start,
          end = end,
          transform = transform
        });
    }

    /// <summary>
    /// This is called when the game should draw itself.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    protected override void Draw(GameTime gameTime)
    {
      GraphicsDevice.Clear(Color.CornflowerBlue);

      base.Draw(gameTime);

      GraphicsDevice.DepthStencilState = DepthStencilState.Default;
      for (int i = 0; i < ship_flyovers.Count; i++)
      {
        var sf = ship_flyovers[i];

        sf.ship_flyover_time -= (float)gameTime.ElapsedGameTime.TotalSeconds;
        if (sf.ship_flyover_time <= 0.0f)
        {
          ship_flyovers.RemoveAt(i);
          i--;
          continue;
        }
        var ship = sf.model;
        ship.EnableShipLighting();
        var bs = (from m in ship.Meshes select m.BoundingSphere).Aggregate(BoundingSphere.CreateMerged);
        var alpha = sf.ship_flyover_time / MAX_SHIP_FLYOVER_TIME;

        ship.Draw(Matrix.CreateTranslation(-bs.Center) *
                  Matrix.CreateScale(1.0f / bs.Radius) *
                  Matrix.CreateRotationZ((float)Math.Sin(alpha * MathHelper.Pi)) *
                  Matrix.CreateRotationY(1.5f) *
                  Matrix.CreateScale(2.0f) *
                  Matrix.CreateTranslation(
                        Vector3.Lerp(sf.start, sf.end, 1.0f - MathHelper.SmoothStep(0.0f, 1.0f, alpha))) *
                  Matrix.CreateRotationY((float)Math.Sin(alpha * MathHelper.Pi)) *
                  sf.transform,
                  Matrix.CreateLookAt(Vector3.Backward * 10.0f, Vector3.Zero, Vector3.Up),
                  Matrix.CreatePerspectiveFieldOfView(1.5f, 4.0f / 3.0f, 0.1f, 10000.0f));
      }

      spriteBatch.Begin();

      if (background_svc.IsRotating == false)
      {
        var curr_slide = slides[current];
        var vp = GraphicsDevice.Viewport;
        if (curr_slide is TextSlide)
        {
          var slide = curr_slide as TextSlide;
          var pfx = slide.use_arrows ? "→ " : "";
          var s = (from b in slide.Bullets select pfx + b + "\n").Aggregate("", String.Concat);
          var ss = font.MeasureString(s);
          spriteBatch.DrawString(font, s, new Vector2(vp.Width - ss.X + 3, vp.Height - ss.Y + 3) * 0.5f, Color.LightBlue);
          spriteBatch.DrawString(font, s, new Vector2(vp.Width - ss.X, vp.Height - ss.Y) * 0.5f, Color.White);


          s = slide.Title;
          ss = font.MeasureString(s);
          spriteBatch.DrawString(font, s, new Vector2(vp.Width - ss.X + 3, vp.Height * 0.1f + 3) * 0.5f, Color.LightBlue);
          spriteBatch.DrawString(font, s, new Vector2(vp.Width - ss.X, vp.Height * 0.1f) * 0.5f, Color.White);


          var small_font = Content.Load<SpriteFont>("OCRAExtended16");
          var progress_string = (current + 1) + @"/" + slides.Count;
          var progress_string_size = small_font.MeasureString(progress_string);
          spriteBatch.DrawString(small_font, progress_string,
            new Vector2(GraphicsDevice.Viewport.Width - 5, GraphicsDevice.Viewport.Height - 5) - progress_string_size,
            Color.White);
          spriteBatch.DrawString(small_font, progress_string,
            new Vector2(GraphicsDevice.Viewport.Width - 4, GraphicsDevice.Viewport.Height - 4) - progress_string_size,
            Color.LightBlue);
        }
        else if (curr_slide is PictureSlide)
        {
          GraphicsDevice.Clear(Color.Black);
          var slide = curr_slide as PictureSlide;
          spriteBatch.Draw(Content.Load<Texture2D>(slide.Path), new Rectangle(0, 0, vp.Width, vp.Height), Color.White);
        }
      }
      if (slides[current] is TextSlide)
      {
        spriteBatch.Draw(Content.Load<Texture2D>("venice_camp_logo"),
          new Rectangle(0, GraphicsDevice.Viewport.Height - GraphicsDevice.Viewport.Height / 15,
                        GraphicsDevice.Viewport.Width / 7, GraphicsDevice.Viewport.Height / 15), Color.White);
      }

      spriteBatch.End();
    }
  }
}
